//////////////////////////////////////////////////////////////////////////
#pragma once
#pragma managed
//////////////////////////////////////////////////////////////////////////
NAMESPACE_BEGIN
//////////////////////////////////////////////////////////////////////////
public ref class ManagedStatModel abstract : public NativeHandleObject
{
protected:
	ManagedStatModel(CvStatModel* model, bool own) : NativeHandleObject(model, own)
	{

	}

	virtual void ReleaseHandle() override
	{
		if (m_pHandle != NULL)
		{
			delete m_pHandle;
		}
	}

internal:
	inline CvStatModel* ToCvStatModelPointer()
	{
		return (CvStatModel*)m_pHandle;
	}

public:
	void Clear()
	{
		((CvStatModel*)m_pHandle)->clear();
	}

	void Save(System::String^ filename)
	{
		((CvStatModel*)m_pHandle)->save(auto_string(filename));
	}

	void Load(System::String^ filename)
	{
		((CvStatModel*)m_pHandle)->load(auto_string(filename));
	}

	void Save(System::String^ filename, System::String^ name)
	{
		((CvStatModel*)m_pHandle)->save(auto_string(filename), auto_string(name));
	}

	void Load(System::String^ filename, System::String^ name)
	{
		((CvStatModel*)m_pHandle)->load(auto_string(filename), auto_string(name));
	}

	void Write(ManagedFileStorage^ storage, System::String^ name)
	{
		((CvStatModel*)m_pHandle)->write(storage->ToCvFileStoragePointer(), auto_string(name));
	}

	void Read(ManagedFileStorage^ storage, ManagedFileNode^ node)
	{
		((CvStatModel*)m_pHandle)->read(storage->ToCvFileStoragePointer(), node->ToCvFileNodePointer());
	}
};

/****************************************************************************************\
*                                 Normal Bayes Classifier                                *
\****************************************************************************************/
public ref class ManagedNormalBayesClassifier : public ManagedStatModel
{
internal:
	ManagedNormalBayesClassifier(CvNormalBayesClassifier* model, bool own) : ManagedStatModel(model, own)
	{

	}

	inline CvNormalBayesClassifier* ToCvNormalBayesClassifierPointer()
	{
		return (CvNormalBayesClassifier*)m_pHandle;
	}

public:
	ManagedNormalBayesClassifier() : ManagedStatModel(new CvNormalBayesClassifier(), true)
	{
	}

	ManagedNormalBayesClassifier(ManagedMat^ _train_data,ManagedMat^ _responses, ManagedMat^ _var_idx, ManagedMat^ _sample_idx)
		: ManagedStatModel(new CvNormalBayesClassifier(
		_train_data->ToCvMatPointer(), _responses->ToCvMatPointer(), _var_idx->ToCvMatPointer(), _sample_idx->ToCvMatPointer()), true)
	{
	}

	ManagedNormalBayesClassifier(ManagedMat^ _train_data,ManagedMat^ _responses, ManagedMat^ _var_idx)
		: ManagedStatModel(new CvNormalBayesClassifier(
		_train_data->ToCvMatPointer(), _responses->ToCvMatPointer(), _var_idx->ToCvMatPointer()), true)
	{
	}

	ManagedNormalBayesClassifier(ManagedMat^ _train_data,ManagedMat^ _responses)
		: ManagedStatModel(new CvNormalBayesClassifier(_train_data->ToCvMatPointer(), _responses->ToCvMatPointer()), true)
	{
	}

	bool Train(ManagedMat^ _train_data,ManagedMat^ _responses, ManagedMat^ _var_idx, ManagedMat^ _sample_idx, bool update)
	{
		return ((CvNormalBayesClassifier*)m_pHandle)->train(
			_train_data->ToCvMatPointer(),
			_responses->ToCvMatPointer(),
			_var_idx->ToCvMatPointer(),
			_sample_idx->ToCvMatPointer(),
			update);
	}

	bool Train(ManagedMat^ _train_data,ManagedMat^ _responses, ManagedMat^ _var_idx, ManagedMat^ _sample_idx)
	{
		return ((CvNormalBayesClassifier*)m_pHandle)->train(
			_train_data->ToCvMatPointer(),
			_responses->ToCvMatPointer(),
			_var_idx->ToCvMatPointer(),
			_sample_idx->ToCvMatPointer());
	}

	bool Train(ManagedMat^ _train_data,ManagedMat^ _responses, ManagedMat^ _var_idx)
	{
		return ((CvNormalBayesClassifier*)m_pHandle)->train(
			_train_data->ToCvMatPointer(),
			_responses->ToCvMatPointer(),
			_var_idx->ToCvMatPointer());
	}

	bool Train(ManagedMat^ _train_data,ManagedMat^ _responses)
	{
		return ((CvNormalBayesClassifier*)m_pHandle)->train(
			_train_data->ToCvMatPointer(),
			_responses->ToCvMatPointer());
	}

	float Predict(ManagedMat^ _samples, ManagedMat^ results)
	{
		return ((CvNormalBayesClassifier*)m_pHandle)->predict(
			_samples->ToCvMatPointer(),
			results->ToCvMatPointer());
	}

	float Predict(ManagedMat^ _samples)
	{
		return ((CvNormalBayesClassifier*)m_pHandle)->predict(_samples->ToCvMatPointer());
	}
};

/****************************************************************************************\
*                          K-Nearest Neighbour Classifier                                *
\****************************************************************************************/
public ref class ManagedKNearest : public ManagedStatModel
{
internal:
	ManagedKNearest(CvKNearest* model, bool own) : ManagedStatModel(model, own)
	{
	}

	inline CvKNearest* ToCvKNearestPointer()
	{
		return (CvKNearest*)m_pHandle;
	}

public:
	ManagedKNearest() : ManagedStatModel(new CvKNearest(), true)
	{
	}

	ManagedKNearest(ManagedMat^ _train_data, ManagedMat^ _responses, ManagedMat^ _sample_idx, bool _is_regression, int max_k)
		: ManagedStatModel(new CvKNearest(
		_train_data->ToCvMatPointer(), _responses->ToCvMatPointer(), _sample_idx->ToCvMatPointer(), _is_regression, max_k), true)
	{
	}

	ManagedKNearest(ManagedMat^ _train_data, ManagedMat^ _responses, ManagedMat^ _sample_idx, bool _is_regression)
		: ManagedStatModel(new CvKNearest(
		_train_data->ToCvMatPointer(), _responses->ToCvMatPointer(), _sample_idx->ToCvMatPointer(), _is_regression), true)
	{
	}

	ManagedKNearest(ManagedMat^ _train_data, ManagedMat^ _responses, ManagedMat^ _sample_idx)
		: ManagedStatModel(new CvKNearest(
		_train_data->ToCvMatPointer(), _responses->ToCvMatPointer(), _sample_idx->ToCvMatPointer()), true)
	{
	}

	ManagedKNearest(ManagedMat^ _train_data, ManagedMat^ _responses)
		: ManagedStatModel(new CvKNearest(_train_data->ToCvMatPointer(), _responses->ToCvMatPointer()), true)
	{
	}

	bool Train(ManagedMat^ _train_data, ManagedMat^ _responses, ManagedMat^ _sample_idx, bool is_regression, int _max_k, bool _update_base)
	{
		return ((CvKNearest*)m_pHandle)->train(
			_train_data->ToCvMatPointer(),
			_responses->ToCvMatPointer(),
			_sample_idx->ToCvMatPointer(),
			is_regression,
			_max_k,
			_update_base);
	}

	bool Train(ManagedMat^ _train_data, ManagedMat^ _responses, ManagedMat^ _sample_idx, bool is_regression, int _max_k)
	{
		return ((CvKNearest*)m_pHandle)->train(
			_train_data->ToCvMatPointer(),
			_responses->ToCvMatPointer(),
			_sample_idx->ToCvMatPointer(),
			is_regression,
			_max_k);
	}

	bool Train(ManagedMat^ _train_data, ManagedMat^ _responses, ManagedMat^ _sample_idx, bool is_regression)
	{
		return ((CvKNearest*)m_pHandle)->train(
			_train_data->ToCvMatPointer(),
			_responses->ToCvMatPointer(),
			_sample_idx->ToCvMatPointer(),
			is_regression);
	}

	bool Train(ManagedMat^ _train_data, ManagedMat^ _responses, ManagedMat^ _sample_idx)
	{
		return ((CvKNearest*)m_pHandle)->train(
			_train_data->ToCvMatPointer(),
			_responses->ToCvMatPointer(),
			_sample_idx->ToCvMatPointer());
	}

	bool Train(ManagedMat^ _train_data, ManagedMat^ _responses)
	{
		return ((CvKNearest*)m_pHandle)->train(
			_train_data->ToCvMatPointer(),
			_responses->ToCvMatPointer());
	}

	float FindNearest(ManagedMat^ _samples, int k, ManagedMat^ results, ManagedMat^ neighbors, ManagedMat^ neighbor_responses, ManagedMat^ dist)
	{
		return ((CvKNearest*)m_pHandle)->find_nearest(
			_samples->ToCvMatPointer(),
			k,
			results->ToCvMatPointer(),
			(const float**)neighbors->ToCvMatPointer(),
			neighbor_responses->ToCvMatPointer(),
			dist->ToCvMatPointer());
	}

	float FindNearest(ManagedMat^ _samples, int k, ManagedMat^ results, ManagedMat^ neighbors, ManagedMat^ neighbor_responses)
	{
		return ((CvKNearest*)m_pHandle)->find_nearest(
			_samples->ToCvMatPointer(),
			k,
			results->ToCvMatPointer(),
			(const float**)neighbors->ToCvMatPointer(),
			neighbor_responses->ToCvMatPointer());
	}

	float FindNearest(ManagedMat^ _samples, int k, ManagedMat^ results, ManagedMat^ neighbors)
	{
		return ((CvKNearest*)m_pHandle)->find_nearest(
			_samples->ToCvMatPointer(),
			k,
			results->ToCvMatPointer(),
			(const float**)neighbors->ToCvMatPointer());
	}

	float FindNearest(ManagedMat^ _samples, int k, ManagedMat^ results)
	{
		return ((CvKNearest*)m_pHandle)->find_nearest(
			_samples->ToCvMatPointer(),
			k,
			results->ToCvMatPointer());
	}

	float FindNearest(ManagedMat^ _samples, int k)
	{
		return ((CvKNearest*)m_pHandle)->find_nearest(
			_samples->ToCvMatPointer(),
			k);
	}

	property int MaxK
	{
		int get() { return ((CvKNearest*)m_pHandle)->get_max_k(); }
	}

	property int VarCount
	{
		int get() { return ((CvKNearest*)m_pHandle)->get_var_count(); }
	}

	property int SampleCount
	{
		int get() { return ((CvKNearest*)m_pHandle)->get_sample_count(); }
	}

	property bool IsRegression
	{
		bool get() { return ((CvKNearest*)m_pHandle)->is_regression(); }
	}
};

/****************************************************************************************\
*                                   Support Vector Machines                              *
\****************************************************************************************/
public ref class ManagedSVM : public ManagedStatModel
{
internal:
	ManagedSVM(CvSVM* model, bool own) : ManagedStatModel(model, own)
	{
	}

	inline CvSVM* ToCvSVMPointer()
	{
		return (CvSVM*)m_pHandle;
	}

public:
	ManagedSVM() : ManagedStatModel(new CvSVM(), true)
	{
	}

	ManagedSVM(ManagedMat^ _train_data, ManagedMat^ _responses, ManagedMat^ _var_idx, ManagedMat^ _sample_idx, SVMParams _params)
		: ManagedStatModel(new CvSVM(
		_train_data->ToCvMatPointer(), _responses->ToCvMatPointer(), _var_idx->ToCvMatPointer(), _sample_idx->ToCvMatPointer(), *(CvSVMParams*)&_params), true)
	{
	}

	ManagedSVM(ManagedMat^ _train_data, ManagedMat^ _responses, ManagedMat^ _var_idx, ManagedMat^ _sample_idx)
		: ManagedStatModel(new CvSVM(
		_train_data->ToCvMatPointer(), _responses->ToCvMatPointer(), _var_idx->ToCvMatPointer(), _sample_idx->ToCvMatPointer()), true)
	{
	}

	ManagedSVM(ManagedMat^ _train_data, ManagedMat^ _responses, ManagedMat^ _var_idx)
		: ManagedStatModel(new CvSVM(
		_train_data->ToCvMatPointer(), _responses->ToCvMatPointer(), _var_idx->ToCvMatPointer()), true)
	{
	}

	ManagedSVM(ManagedMat^ _train_data, ManagedMat^ _responses)
		: ManagedStatModel(new CvSVM(_train_data->ToCvMatPointer(), _responses->ToCvMatPointer()), true)
	{
	}

	bool Train(ManagedMat^ _train_data, ManagedMat^ _responses, ManagedMat^ _var_idx, ManagedMat^ _sample_idx, SVMParams _params)
	{
		return ((CvSVM*)m_pHandle)->train(
			_train_data->ToCvMatPointer(),
			_responses->ToCvMatPointer(),
			_var_idx->ToCvMatPointer(),
			_sample_idx->ToCvMatPointer(),
			*(CvSVMParams*)&_params);
	}

	bool Train(ManagedMat^ _train_data, ManagedMat^ _responses, ManagedMat^ _var_idx, ManagedMat^ _sample_idx)
	{
		return ((CvSVM*)m_pHandle)->train(
			_train_data->ToCvMatPointer(),
			_responses->ToCvMatPointer(),
			_var_idx->ToCvMatPointer(),
			_sample_idx->ToCvMatPointer());
	}

	bool Train(ManagedMat^ _train_data, ManagedMat^ _responses, ManagedMat^ _var_idx)
	{
		return ((CvSVM*)m_pHandle)->train(
			_train_data->ToCvMatPointer(),
			_responses->ToCvMatPointer(),
			_var_idx->ToCvMatPointer());
	}

	bool Train(ManagedMat^ _train_data, ManagedMat^ _responses)
	{
		return ((CvSVM*)m_pHandle)->train(
			_train_data->ToCvMatPointer(),
			_responses->ToCvMatPointer());
	}

	bool TrainAuto(ManagedMat^ _train_data, ManagedMat^ _responses, ManagedMat^ _var_idx, ManagedMat^ _sample_idx, SVMParams _params,
		int k_fold, ParamGrid C_grid, ParamGrid gamma_grid, ParamGrid p_grid, ParamGrid nu_grid, ParamGrid coef_grid, ParamGrid degree_grid)
	{
		return ((CvSVM*)m_pHandle)->train_auto(
			_train_data->ToCvMatPointer(),
			_responses->ToCvMatPointer(),
			_var_idx->ToCvMatPointer(),
			_sample_idx->ToCvMatPointer(),
			*(CvSVMParams*)&_params,
			k_fold,
			*(CvParamGrid*)&C_grid,
			*(CvParamGrid*)&gamma_grid,
			*(CvParamGrid*)&p_grid,
			*(CvParamGrid*)&nu_grid,
			*(CvParamGrid*)&coef_grid, 
			*(CvParamGrid*)&degree_grid);
	}

	bool TrainAuto(ManagedMat^ _train_data, ManagedMat^ _responses, ManagedMat^ _var_idx, ManagedMat^ _sample_idx, SVMParams _params,
		int k_fold, ParamGrid C_grid, ParamGrid gamma_grid, ParamGrid p_grid, ParamGrid nu_grid, ParamGrid coef_grid)
	{
		return ((CvSVM*)m_pHandle)->train_auto(
			_train_data->ToCvMatPointer(),
			_responses->ToCvMatPointer(),
			_var_idx->ToCvMatPointer(),
			_sample_idx->ToCvMatPointer(),
			*(CvSVMParams*)&_params,
			k_fold,
			*(CvParamGrid*)&C_grid,
			*(CvParamGrid*)&gamma_grid,
			*(CvParamGrid*)&p_grid,
			*(CvParamGrid*)&nu_grid,
			*(CvParamGrid*)&coef_grid);
	}

	bool TrainAuto(ManagedMat^ _train_data, ManagedMat^ _responses, ManagedMat^ _var_idx, ManagedMat^ _sample_idx, SVMParams _params,
		int k_fold, ParamGrid C_grid, ParamGrid gamma_grid, ParamGrid p_grid, ParamGrid nu_grid)
	{
		return ((CvSVM*)m_pHandle)->train_auto(
			_train_data->ToCvMatPointer(),
			_responses->ToCvMatPointer(),
			_var_idx->ToCvMatPointer(),
			_sample_idx->ToCvMatPointer(),
			*(CvSVMParams*)&_params,
			k_fold,
			*(CvParamGrid*)&C_grid,
			*(CvParamGrid*)&gamma_grid,
			*(CvParamGrid*)&p_grid,
			*(CvParamGrid*)&nu_grid);
	}

	bool TrainAuto(ManagedMat^ _train_data, ManagedMat^ _responses, ManagedMat^ _var_idx, ManagedMat^ _sample_idx, SVMParams _params,
		int k_fold, ParamGrid C_grid, ParamGrid gamma_grid, ParamGrid p_grid)
	{
		return ((CvSVM*)m_pHandle)->train_auto(
			_train_data->ToCvMatPointer(),
			_responses->ToCvMatPointer(),
			_var_idx->ToCvMatPointer(),
			_sample_idx->ToCvMatPointer(),
			*(CvSVMParams*)&_params,
			k_fold,
			*(CvParamGrid*)&C_grid,
			*(CvParamGrid*)&gamma_grid,
			*(CvParamGrid*)&p_grid);
	}

	bool TrainAuto(ManagedMat^ _train_data, ManagedMat^ _responses, ManagedMat^ _var_idx, ManagedMat^ _sample_idx, SVMParams _params,
		int k_fold, ParamGrid C_grid, ParamGrid gamma_grid)
	{
		return ((CvSVM*)m_pHandle)->train_auto(
			_train_data->ToCvMatPointer(),
			_responses->ToCvMatPointer(),
			_var_idx->ToCvMatPointer(),
			_sample_idx->ToCvMatPointer(),
			*(CvSVMParams*)&_params,
			k_fold,
			*(CvParamGrid*)&C_grid,
			*(CvParamGrid*)&gamma_grid);
	}

	bool TrainAuto(ManagedMat^ _train_data, ManagedMat^ _responses, ManagedMat^ _var_idx, ManagedMat^ _sample_idx, SVMParams _params,
		int k_fold, ParamGrid C_grid)
	{
		return ((CvSVM*)m_pHandle)->train_auto(
			_train_data->ToCvMatPointer(),
			_responses->ToCvMatPointer(),
			_var_idx->ToCvMatPointer(),
			_sample_idx->ToCvMatPointer(),
			*(CvSVMParams*)&_params,
			k_fold,
			*(CvParamGrid*)&C_grid);
	}

	bool TrainAuto(ManagedMat^ _train_data, ManagedMat^ _responses, ManagedMat^ _var_idx, ManagedMat^ _sample_idx, SVMParams _params,
		int k_fold)
	{
		return ((CvSVM*)m_pHandle)->train_auto(
			_train_data->ToCvMatPointer(),
			_responses->ToCvMatPointer(),
			_var_idx->ToCvMatPointer(),
			_sample_idx->ToCvMatPointer(),
			*(CvSVMParams*)&_params,
			k_fold);
	}

	bool TrainAuto(ManagedMat^ _train_data, ManagedMat^ _responses, ManagedMat^ _var_idx, ManagedMat^ _sample_idx, SVMParams _params)
	{
		return ((CvSVM*)m_pHandle)->train_auto(
			_train_data->ToCvMatPointer(),
			_responses->ToCvMatPointer(),
			_var_idx->ToCvMatPointer(),
			_sample_idx->ToCvMatPointer(),
			*(CvSVMParams*)&_params);
	}

	float Predict(ManagedMat^ _sample, bool returnDFVal)
	{
		return ((CvSVM*)m_pHandle)->predict(_sample->ToCvMatPointer(), returnDFVal);
	}

	float Predict(ManagedMat^ _sample)
	{
		return ((CvSVM*)m_pHandle)->predict(_sample->ToCvMatPointer());
	}

	property int SupportVectorCount
	{
		int get() { return ((CvSVM*)m_pHandle)->get_support_vector_count(); }
	}

	array<float>^ GetSupportVector(int i)
	{
		const float* psv = ((CvSVM*)m_pHandle)->get_support_vector(i);
		array<float>^ arrsv = nullptr;
		if (psv != NULL)
		{
			array<float>^ arrsv = gcnew array<float>(((CvSVM*)m_pHandle)->get_var_count());
			for (int j = 0; j < arrsv->Length; j++)
			{
				arrsv[j] = psv[j];
			}
		}
		return arrsv;
	}

	property SVMParams Params
	{
		SVMParams get() { return *(SVMParams*)&(((CvSVM*)m_pHandle)->get_params()); }
	}

	static ParamGrid GetDefaultGrid(int param_id)
	{
		return *(ParamGrid*)&(CvSVM::get_default_grid((int)param_id));
	}

	property int VarCount
	{
		int get() { return ((CvSVM*)m_pHandle)->get_var_count(); }
	}
};

/****************************************************************************************\
*                              Expectation - Maximization                                *
\****************************************************************************************/
public ref class ManagedEM : public ManagedStatModel
{
internal:
	ManagedEM(CvEM* model, bool own) : ManagedStatModel(model, own)
	{
	}

	inline CvEM* ToCvEMPointer()
	{
		return (CvEM*)m_pHandle;
	}

public:
	ManagedEM() : ManagedStatModel(new CvEM(), true)
	{
	}

	ManagedEM(ManagedMat^ samples, ManagedMat^ sample_idx, EMParams params, ManagedMat^ labels)
		: ManagedStatModel(new CvEM(samples->ToCvMatPointer(), sample_idx->ToCvMatPointer(), *(CvEMParams*)&params, labels->ToCvMatPointer()), true)
	{
	}

	ManagedEM(ManagedMat^ samples, ManagedMat^ sample_idx, EMParams params)
		: ManagedStatModel(new CvEM(samples->ToCvMatPointer(), sample_idx->ToCvMatPointer(), *(CvEMParams*)&params), true)
	{
	}

	ManagedEM(ManagedMat^ samples, ManagedMat^ sample_idx)
		: ManagedStatModel(new CvEM(samples->ToCvMatPointer(), sample_idx->ToCvMatPointer()), true)
	{
	}

	ManagedEM(ManagedMat^ samples)
		: ManagedStatModel(new CvEM(samples->ToCvMatPointer()), true)
	{
	}

	bool Train(ManagedMat^ samples, ManagedMat^ sample_idx, EMParams params, ManagedMat^ labels)
	{
		return ((CvEM*)m_pHandle)->train(samples->ToCvMatPointer(), sample_idx->ToCvMatPointer(), *(CvEMParams*)&params, labels->ToCvMatPointer());
	}

	bool Train(ManagedMat^ samples, ManagedMat^ sample_idx, EMParams params)
	{
		return ((CvEM*)m_pHandle)->train(samples->ToCvMatPointer(), sample_idx->ToCvMatPointer(), *(CvEMParams*)&params);
	}

	bool Train(ManagedMat^ samples, ManagedMat^ sample_idx)
	{
		return ((CvEM*)m_pHandle)->train(samples->ToCvMatPointer(), sample_idx->ToCvMatPointer());
	}

	bool Train(ManagedMat^ samples)
	{
		return ((CvEM*)m_pHandle)->train(samples->ToCvMatPointer());
	}

	float Predict(ManagedMat^ sample,ManagedMat^ probs)
	{
		return ((CvEM*)m_pHandle)->predict(sample->ToCvMatPointer(), probs->ToCvMatPointer());
	}

	property int NClusters
	{
		int get() { return ((CvEM*)m_pHandle)->get_nclusters(); }
	}

	property ManagedMat^ Means
	{
		ManagedMat^ get() { return gcnew ManagedMat((CvMat*)((CvEM*)m_pHandle)->get_means(), false); }
	}

	property ManagedMat^ Covs
	{
		ManagedMat^ get() { return gcnew ManagedMat((CvMat*)((CvEM*)m_pHandle)->get_covs(), false); }
	}

	property ManagedMat^ Weights
	{
		ManagedMat^ get() { return gcnew ManagedMat((CvMat*)((CvEM*)m_pHandle)->get_weights(), false); }
	}

	property ManagedMat^ Probs
	{
		ManagedMat^ get() { return gcnew ManagedMat((CvMat*)((CvEM*)m_pHandle)->get_probs(), false); }
	}

	property double LogLikelihood
	{
		double get() { return ((CvEM*)m_pHandle)->get_log_likelihood(); }
	}
};

/****************************************************************************************\
*                                      Decision Tree                                     *
\****************************************************************************************/\
public ref class ManagedDTreeTrainData : NativeHandleObject
{
public:
	ManagedDTreeTrainData() : NativeHandleObject(new CvDTreeTrainData(), true)
	{
	}

protected:
	virtual void ReleaseHandle() override
	{
		delete m_pHandle;
	}

internal:
	ManagedDTreeTrainData(CvDTreeTrainData* handle, bool own) : NativeHandleObject(handle, own)
	{
	}

	CvDTreeTrainData* ToCvDTreeTrainDataPointer()
	{
		return (CvDTreeTrainData*)m_pHandle; 
	}
};

public ref class ManagedDTreeNode : NativeHandleObject
{
protected:
	virtual void ReleaseHandle() override
	{
	}

internal:
	ManagedDTreeNode(CvDTreeNode* handle) : NativeHandleObject(handle, false)
	{
	}

	CvDTreeNode* GetCvDTreeNodePointer()
	{
		return (CvDTreeNode*)m_pHandle; 
	}
};


public ref class ManagedMLData : NativeHandleObject
{
protected:
	virtual void ReleaseHandle() override
	{
		delete m_pHandle;
	}

internal:
	ManagedMLData(CvMLData* data, bool own) : NativeHandleObject(data, own)
	{

	}

	CvMLData* ToCvMLDataPointer()
	{
		return (CvMLData*)m_pHandle; 
	}

public:
	ManagedMLData() : NativeHandleObject(new CvMLData(), true)
	{
	}

	// returns:
	// 0 - OK  
	// 1 - file can not be opened or is not correct
	bool ReadCsv(System::String^ filename)
	{
		return ((CvMLData*)m_pHandle)->read_csv(auto_string(filename)) == 0;
	}

	property ManagedMat^ Values
	{
		ManagedMat^ get() { return gcnew ManagedMat((CvMat*)((CvMLData*)m_pHandle)->get_values(), false); }
	}

	property ManagedMat^ Responses
	{
		ManagedMat^ get() { return gcnew ManagedMat((CvMat*)((CvMLData*)m_pHandle)->get_responses(), false); }
	}

	property ManagedMat^  Missing
	{
		ManagedMat^ get() { return gcnew ManagedMat((CvMat*)((CvMLData*)m_pHandle)->get_missing(), false); }
	}

	property int ResponseIdx
	{
		int get() { return ((CvMLData*)m_pHandle)->get_response_idx(); }
		void set(int idx) { ((CvMLData*)m_pHandle)->set_response_idx(idx); }
	}

	property ManagedMat^ TrainSampleIdx
	{
		ManagedMat^ get() { return gcnew ManagedMat((CvMat*)((CvMLData*)m_pHandle)->get_train_sample_idx(), false); }
	}

	property ManagedMat^ TestSampleIdx
	{
		ManagedMat^ get() { return gcnew ManagedMat((CvMat*)((CvMLData*)m_pHandle)->get_test_sample_idx(), false); }
	}

	void MixTrainAndTestIdx()
	{
		return ((CvMLData*)m_pHandle)->mix_train_and_test_idx();
	}

	property NAMESPACE::TrainTestSplit TrainTestSplit
	{
		void set(NAMESPACE::TrainTestSplit spl) { ((CvMLData*)m_pHandle)->set_train_test_split((CvTrainTestSplit*)&spl); }
	}

	property ManagedMat^ VarIdx
	{
		ManagedMat^ get() { return gcnew ManagedMat((CvMat*)((CvMLData*)m_pHandle)->get_missing(), false); }
	}

	void ChangeVarIdx(int vi, bool state)
	{
		return ((CvMLData*)m_pHandle)->chahge_var_idx(vi, state);
	}

	property ManagedMat^ VarTypes
	{
		ManagedMat^ get() { return gcnew ManagedMat((CvMat*)((CvMLData*)m_pHandle)->get_missing(), false); }
	}

	int GetVarType(int var_idx)
	{
		return ((CvMLData*)m_pHandle)->get_var_type(var_idx); 
	}

	// following 2 methods enable to change vars type
	// use these methods to assign CV_VAR_CATEGORICAL type for categorical variable
	// with numerical labels; in the other cases var types are correctly determined automatically
	void SetVarTypes(System::String^ str)
	{
		((CvMLData*)m_pHandle)->set_var_types(auto_string(str));
	}

	// "ord[0-17],cat[18]", "ord[0,2,4,10-12], cat[1,3,5-9,13,14]",
	// "cat", "ord" (all vars are categorical/ordered)
	void ChangeVarType(int var_idx, MLVariableType type)
	{
		return ((CvMLData*)m_pHandle)->change_var_type(var_idx, (int)type);
	}

	property char Delimiter
	{
		char get() { return ((CvMLData*)m_pHandle)->get_delimiter(); }
		void set(char ch) { return ((CvMLData*)m_pHandle)->set_delimiter(ch); }
	}

	property char MissCh
	{
		char get() { return ((CvMLData*)m_pHandle)->get_miss_ch(); }
		void set(char ch) { return ((CvMLData*)m_pHandle)->set_miss_ch(ch); }
	}
};

public ref class ManagedDTree : public ManagedStatModel
{
internal:
	ManagedDTree(CvDTree* model, bool own) : ManagedStatModel(model, own)
	{
	}

	inline CvDTree* ToCvDTreePointer()
	{
		return (CvDTree*)m_pHandle;
	}

public:
	ManagedDTree() : ManagedStatModel(new CvDTree(), true)
	{
	}

	bool Train(ManagedMat^ _train_data, 
		int _tflag,
		ManagedMat^ _responses, 
		ManagedMat^ _var_idx,
		ManagedMat^ _sample_idx, 
		ManagedMat^ _var_type,
		ManagedMat^ _missing_mask,
		DTreeParams params)
	{
		return ((CvDTree*)m_pHandle)->train(
			_train_data->ToCvMatPointer(), 
			_tflag,
			_responses->ToCvMatPointer(), 
			_var_idx->ToCvMatPointer(),
			_sample_idx->ToCvMatPointer(), 
			_var_type->ToCvMatPointer(),
			_missing_mask->ToCvMatPointer(),
			*(CvDTreeParams*)&params);
	}

	bool Train(ManagedMat^ _train_data, 
		int _tflag,
		ManagedMat^ _responses, 
		ManagedMat^ _var_idx,
		ManagedMat^ _sample_idx, 
		ManagedMat^ _var_type,
		ManagedMat^ _missing_mask)
	{
		return ((CvDTree*)m_pHandle)->train(
			_train_data->ToCvMatPointer(), 
			_tflag,
			_responses->ToCvMatPointer(), 
			_var_idx->ToCvMatPointer(),
			_sample_idx->ToCvMatPointer(), 
			_var_type->ToCvMatPointer(),
			_missing_mask->ToCvMatPointer());
	}

	bool Train(ManagedMat^ _train_data, 
		int _tflag,
		ManagedMat^ _responses, 
		ManagedMat^ _var_idx,
		ManagedMat^ _sample_idx, 
		ManagedMat^ _var_type)
	{
		return ((CvDTree*)m_pHandle)->train(
			_train_data->ToCvMatPointer(), 
			_tflag,
			_responses->ToCvMatPointer(), 
			_var_idx->ToCvMatPointer(),
			_sample_idx->ToCvMatPointer(), 
			_var_type->ToCvMatPointer());
	}

	bool Train(ManagedMat^ _train_data, 
		int _tflag,
		ManagedMat^ _responses, 
		ManagedMat^ _var_idx,
		ManagedMat^ _sample_idx)
	{
		return ((CvDTree*)m_pHandle)->train(
			_train_data->ToCvMatPointer(), 
			_tflag,
			_responses->ToCvMatPointer(), 
			_var_idx->ToCvMatPointer(),
			_sample_idx->ToCvMatPointer());
	}

	bool Train(ManagedMat^ _train_data, int _tflag, ManagedMat^ _responses, ManagedMat^ _var_idx)
	{
		return ((CvDTree*)m_pHandle)->train(_train_data->ToCvMatPointer(), _tflag, _responses->ToCvMatPointer(), _var_idx->ToCvMatPointer());
	}

	bool Train(ManagedMat^ _train_data, int _tflag, ManagedMat^ _responses)
	{
		return ((CvDTree*)m_pHandle)->train(_train_data->ToCvMatPointer(), _tflag, _responses->ToCvMatPointer());
	}

	bool Train(ManagedMLData^ _data, DTreeParams _params)
	{
		return ((CvDTree*)m_pHandle)->train(_data->ToCvMLDataPointer(), *(CvDTreeParams*)&_params);
	}

	bool Train(ManagedMLData^ _data)
	{
		return ((CvDTree*)m_pHandle)->train(_data->ToCvMLDataPointer());
	}

	float CalcError(ManagedMLData^ _data, MlErrorType type , ManagedMat^ resp)
	{
		return ((CvDTree*)m_pHandle)->calc_error(_data->ToCvMLDataPointer(), (int)type, (vector<float>*)resp->ToCvMatPointer());
	}

	float CalcError(ManagedMLData^ _data, MlErrorType type)
	{
		return ((CvDTree*)m_pHandle)->calc_error(_data->ToCvMLDataPointer(), (int)type);
	}

	bool Train(ManagedMat^ _train_data, ManagedMat^ _subsample_idx)
	{
		return ((CvDTree*)m_pHandle)->train((CvDTreeTrainData*)_train_data->ToCvMatPointer(), _subsample_idx->ToCvMatPointer());
	}

	ManagedDTreeNode^ Predict(ManagedMat^ _sample, ManagedMat^ _missing_data_mask, bool preprocessed_input)
	{
		return gcnew ManagedDTreeNode((CvDTreeNode*)((CvDTree*)m_pHandle)->predict(_sample->ToCvMatPointer(), _missing_data_mask->ToCvMatPointer(), preprocessed_input));
	}

	ManagedDTreeNode^ Predict(ManagedMat^ _sample, ManagedMat^ _missing_data_mask)
	{
		return gcnew ManagedDTreeNode((CvDTreeNode*)((CvDTree*)m_pHandle)->predict(_sample->ToCvMatPointer(), _missing_data_mask->ToCvMatPointer()));
	}

	ManagedDTreeNode^ Predict(ManagedMat^ _sample)
	{
		return gcnew ManagedDTreeNode((CvDTreeNode*)((CvDTree*)m_pHandle)->predict(_sample->ToCvMatPointer()));
	}

	property ManagedMat^ VarImportance
	{
		ManagedMat^ get() { return gcnew ManagedMat((CvMat*)((CvDTree*)m_pHandle)->get_var_importance(), false); }
	}

	// special read & write methods for trees in the tree ensembles
	void Read(ManagedFileStorage^ fs, ManagedFileNode^ node, ManagedDTreeTrainData^ data)
	{
		return ((CvDTree*)m_pHandle)->read(fs->ToCvFileStoragePointer(), node->ToCvFileNodePointer(), data->ToCvDTreeTrainDataPointer());
	}

	void Write(ManagedFileStorage^ fs)
	{
		return ((CvDTree*)m_pHandle)->write(fs->ToCvFileStoragePointer());
	}

	property ManagedDTreeNode^ Root
	{
		ManagedDTreeNode^ get() { return gcnew ManagedDTreeNode((CvDTreeNode*)((CvDTree*)m_pHandle)->get_root()); }
	}

	property int PrunedTreeIdx
	{
		int get() { return ((CvDTree*)m_pHandle)->get_pruned_tree_idx(); }
	}

	property ManagedDTreeTrainData^ Data
	{
		ManagedDTreeTrainData^ get() { return gcnew ManagedDTreeTrainData(((CvDTree*)m_pHandle)->get_data(), false); }
	}
};

/****************************************************************************************\
*                                   Random Trees Classifier                              *
\****************************************************************************************/
ref class ManagedForestTree;

public ref class ManagedRTrees : public ManagedStatModel
{
internal:
	ManagedRTrees(CvRTrees* model, bool own) : ManagedStatModel(model, own)
	{
	}

	inline CvRTrees* ToCvRTreesPointer()
	{
		return (CvRTrees*)m_pHandle;
	}

public:
	ManagedRTrees() : ManagedStatModel(new CvRTrees(), true)
	{
	}

	bool Train(ManagedMat^ _train_data, 
		int _tflag,
		ManagedMat^ _responses, 
		ManagedMat^ _var_idx,
		ManagedMat^ _sample_idx, 
		ManagedMat^ _var_type,
		ManagedMat^ _missing_mask,
		RTParams params)
	{
		return ((CvRTrees*)m_pHandle)->train(
			_train_data->ToCvMatPointer(), 
			_tflag,
			_responses->ToCvMatPointer(), 
			_var_idx->ToCvMatPointer(),
			_sample_idx->ToCvMatPointer(), 
			_var_type->ToCvMatPointer(),
			_missing_mask->ToCvMatPointer(),
			*(CvRTParams*)&params);
	}

	bool Train(ManagedMat^ _train_data, 
		int _tflag,
		ManagedMat^ _responses, 
		ManagedMat^ _var_idx,
		ManagedMat^ _sample_idx, 
		ManagedMat^ _var_type,
		ManagedMat^ _missing_mask)
	{
		return ((CvRTrees*)m_pHandle)->train(
			_train_data->ToCvMatPointer(), 
			_tflag,
			_responses->ToCvMatPointer(), 
			_var_idx->ToCvMatPointer(),
			_sample_idx->ToCvMatPointer(), 
			_var_type->ToCvMatPointer(),
			_missing_mask->ToCvMatPointer());
	}

	bool Train(ManagedMat^ _train_data, 
		int _tflag,
		ManagedMat^ _responses, 
		ManagedMat^ _var_idx,
		ManagedMat^ _sample_idx, 
		ManagedMat^ _var_type)
	{
		return ((CvRTrees*)m_pHandle)->train(
			_train_data->ToCvMatPointer(), 
			_tflag,
			_responses->ToCvMatPointer(), 
			_var_idx->ToCvMatPointer(),
			_sample_idx->ToCvMatPointer(), 
			_var_type->ToCvMatPointer());
	}

	bool Train(ManagedMat^ _train_data, 
		int _tflag,
		ManagedMat^ _responses, 
		ManagedMat^ _var_idx,
		ManagedMat^ _sample_idx)
	{
		return ((CvRTrees*)m_pHandle)->train(
			_train_data->ToCvMatPointer(), 
			_tflag,
			_responses->ToCvMatPointer(), 
			_var_idx->ToCvMatPointer(),
			_sample_idx->ToCvMatPointer());
	}

	bool Train(ManagedMat^ _train_data, 
		int _tflag,
		ManagedMat^ _responses, 
		ManagedMat^ _var_idx)
	{
		return ((CvRTrees*)m_pHandle)->train(
			_train_data->ToCvMatPointer(), 
			_tflag,
			_responses->ToCvMatPointer(), 
			_var_idx->ToCvMatPointer());
	}

	bool Train(ManagedMat^ _train_data, int _tflag, ManagedMat^ _responses)
	{
		return ((CvRTrees*)m_pHandle)->train(
			_train_data->ToCvMatPointer(), 
			_tflag,
			_responses->ToCvMatPointer());
	}

	bool Train(ManagedMLData^ data, RTParams params)
	{
		return ((CvRTrees*)m_pHandle)->train(data->ToCvMLDataPointer(), *(CvRTParams*)&params);
	}

	bool Train(ManagedMLData^ data)
	{
		return ((CvRTrees*)m_pHandle)->train(data->ToCvMLDataPointer());
	}

	float Predict(ManagedMat^ sample, ManagedMat^ missing)
	{
		return ((CvRTrees*)m_pHandle)->predict(sample->ToCvMatPointer(), missing->ToCvMatPointer());
	}

	float Predict(ManagedMat^ sample)
	{
		return ((CvRTrees*)m_pHandle)->predict(sample->ToCvMatPointer());
	}

	float PredictProb(ManagedMat^ sample, ManagedMat^ missing)
	{
		return ((CvRTrees*)m_pHandle)->predict_prob(sample->ToCvMatPointer(), missing->ToCvMatPointer());
	}

	float PredictProb(ManagedMat^ sample)
	{
		return ((CvRTrees*)m_pHandle)->predict_prob(sample->ToCvMatPointer());
	}

	property ManagedMat^ VarImportance
	{
		ManagedMat^ get() { return gcnew ManagedMat((CvMat*)((CvRTrees*)m_pHandle)->get_var_importance(), false); }
	}

	float proximity(ManagedMat^ sample1, ManagedMat^ sample2, ManagedMat^ missing1,  ManagedMat^ missing2)
	{
		return ((CvRTrees*)m_pHandle)->get_proximity(
			sample1->ToCvMatPointer(), 
			sample2->ToCvMatPointer(), 
			missing1->ToCvMatPointer(), 
			missing2->ToCvMatPointer());
	}

	float proximity(ManagedMat^ sample1, ManagedMat^ sample2, ManagedMat^ missing1)
	{
		return ((CvRTrees*)m_pHandle)->get_proximity(
			sample1->ToCvMatPointer(), 
			sample2->ToCvMatPointer(), 
			missing1->ToCvMatPointer());
	}

	float proximity(ManagedMat^ sample1, ManagedMat^ sample2)
	{
		return ((CvRTrees*)m_pHandle)->get_proximity(
			sample1->ToCvMatPointer(), 
			sample2->ToCvMatPointer());
	}

	float CalcError(ManagedMLData^ _data, MlErrorType type, ManagedMat^ resp)
	{
		return ((CvRTrees*)m_pHandle)->calc_error(
			_data->ToCvMLDataPointer(), 
			(int)type, 
			(vector<float>*)resp->ToCvMatPointer());
	}

	float CalcError(ManagedMLData^ _data, MlErrorType type)
	{
		return ((CvRTrees*)m_pHandle)->calc_error(
			_data->ToCvMLDataPointer(), 
			(int)type);
	}

	property float TrainError
	{
		float get() { return ((CvRTrees*)m_pHandle)->get_train_error(); }
	}

	property ManagedMat^ ActiveVarMask
	{
		ManagedMat^ get() { return gcnew ManagedMat(((CvRTrees*)m_pHandle)->get_active_var_mask(), false); }
	}

	property NAMESPACE::Rng Rng
	{
		NAMESPACE::Rng get() { return *(NAMESPACE::Rng*)(((CvRTrees*)m_pHandle)->get_rng()); }
	}

	property int TreeCount
	{
		int get() { return ((CvRTrees*)m_pHandle)->get_tree_count(); }
	}

	ManagedForestTree^ GetTree(int i);
};

public ref class ManagedForestTree : ManagedDTree
{
internal:
	ManagedForestTree(CvForestTree* model, bool own) : ManagedDTree(model, own)
	{
	}

	inline CvForestTree* ToCvForestTreePointer()
	{
		return (CvForestTree*)m_pHandle;
	}

public:
	ManagedForestTree() : ManagedDTree(new CvForestTree(), true)
	{
	}

	bool Train(ManagedDTreeTrainData^ _train_data, ManagedMat^ _subsample_idx, ManagedRTrees^ forest)
	{
		return ((CvForestTree*)m_pHandle)->train(_train_data->ToCvDTreeTrainDataPointer(), 
			_subsample_idx->ToCvMatPointer(), forest->ToCvRTreesPointer());
	}

	property int VarCount 
	{
		int get() { return ((CvForestTree*)m_pHandle)->get_var_count(); }
	}

	void Read(ManagedFileStorage^ fs, ManagedFileNode^ node, ManagedRTrees^ forest, ManagedDTreeTrainData^ _data)
	{
		return ((CvForestTree*)m_pHandle)->read(
			fs->ToCvFileStoragePointer(),
			node->ToCvFileNodePointer(),
			forest->ToCvRTreesPointer(),
			_data->ToCvDTreeTrainDataPointer());
	}
};

ManagedForestTree^ ManagedRTrees::GetTree(int i)
{
	return gcnew ManagedForestTree(((CvRTrees*)m_pHandle)->get_tree(i), false);
}

/****************************************************************************************\
*                           Extremely randomized trees Classifier                        *
\****************************************************************************************/
public ref class ManagedERTreeTrainData : public ManagedDTreeTrainData
{
public:
	ManagedERTreeTrainData() : ManagedDTreeTrainData(new CvERTreeTrainData(), true)
	{

	}
};

public ref class ManagedForestERTree : public ManagedForestTree
{
internal:
	ManagedForestERTree(CvForestERTree* model, bool own) : ManagedForestTree(model, own)
	{
	}

	inline CvForestERTree* ToCvForestERTreePointer()
	{
		return (CvForestERTree*)m_pHandle;
	}

public:
	ManagedForestERTree()
	{
		m_pHandle = new CvForestERTree();
	}
};

public ref class ManagedERTrees : public ManagedRTrees
{
internal:
	ManagedERTrees(CvERTrees* model, bool own) : ManagedRTrees(model, own)
	{
	}

	inline CvERTrees* ToCvERTreesPointer()
	{
		return (CvERTrees*)m_pHandle;
	}

public:
	ManagedERTrees() : ManagedRTrees(new CvERTrees(), true)
	{
	}
};

/****************************************************************************************\
*                                   Boosted tree classifier                              *
\****************************************************************************************/

public ref class ManagedBoost : public ManagedStatModel
{
internal:
	ManagedBoost(CvBoost* model, bool own) : ManagedStatModel(model, own)
	{
	}

	inline CvBoost* ToCvBoostPointer()
	{
		return (CvBoost*)m_pHandle;
	}

public:
	ManagedBoost() : ManagedStatModel(new CvBoost(), true)
	{
	}

	ManagedBoost(ManagedMat^ _train_data, int _tflag, ManagedMat^ _responses, ManagedMat^ _var_idx, ManagedMat^ _sample_idx, ManagedMat^ _var_type, ManagedMat^ _missing_mask, BoostParams params)
		: ManagedStatModel(new CvBoost(
		_train_data->ToCvMatPointer(), _tflag, _responses->ToCvMatPointer(), _var_idx->ToCvMatPointer(), _sample_idx->ToCvMatPointer(), _var_type->ToCvMatPointer(), _missing_mask->ToCvMatPointer(), *(CvBoostParams*)&params), true)
	{
	}

	ManagedBoost(ManagedMat^ _train_data, int _tflag, ManagedMat^ _responses, ManagedMat^ _var_idx, ManagedMat^ _sample_idx, ManagedMat^ _var_type, ManagedMat^ _missing_mask)
		: ManagedStatModel(new CvBoost(
		_train_data->ToCvMatPointer(), _tflag, _responses->ToCvMatPointer(), _var_idx->ToCvMatPointer(), _sample_idx->ToCvMatPointer(), _var_type->ToCvMatPointer(), _missing_mask->ToCvMatPointer()), true)
	{
	}

	ManagedBoost(ManagedMat^ _train_data,  int _tflag, ManagedMat^ _responses,  ManagedMat^ _var_idx, ManagedMat^ _sample_idx,  ManagedMat^ _var_type)
		: ManagedStatModel(new CvBoost(
		_train_data->ToCvMatPointer(), _tflag, _responses->ToCvMatPointer(), _var_idx->ToCvMatPointer(), _sample_idx->ToCvMatPointer(), _var_type->ToCvMatPointer()), true)
	{
	}

	ManagedBoost(ManagedMat^ _train_data,  int _tflag, ManagedMat^ _responses,  ManagedMat^ _var_idx, ManagedMat^ _sample_idx)
		: ManagedStatModel(new CvBoost(
		_train_data->ToCvMatPointer(), _tflag, _responses->ToCvMatPointer(), _var_idx->ToCvMatPointer(), _sample_idx->ToCvMatPointer()), true)
	{
	}

	ManagedBoost(ManagedMat^ _train_data, int _tflag, ManagedMat^ _responses, ManagedMat^ _var_idx)
		: ManagedStatModel(new CvBoost(
		_train_data->ToCvMatPointer(), _tflag, _responses->ToCvMatPointer(), _var_idx->ToCvMatPointer()), true)
	{
	}

	ManagedBoost(ManagedMat^ _train_data, int _tflag, ManagedMat^ _responses)
		: ManagedStatModel(new CvBoost(_train_data->ToCvMatPointer(), _tflag, _responses->ToCvMatPointer()), true)
	{
	}

	bool Train(ManagedMat^ _train_data, 
		int _tflag,
		ManagedMat^ _responses, 
		ManagedMat^ _var_idx,
		ManagedMat^ _sample_idx, 
		ManagedMat^ _var_type,
		ManagedMat^ _missing_mask,
		BoostParams params,
		bool update)
	{
		return ((CvBoost*)m_pHandle)->train(
			_train_data->ToCvMatPointer(), 
			_tflag,
			_responses->ToCvMatPointer(), 
			_var_idx->ToCvMatPointer(),
			_sample_idx->ToCvMatPointer(), 
			_var_type->ToCvMatPointer(),
			_missing_mask->ToCvMatPointer(),
			*(CvBoostParams*)&params,
			update);
	}

	bool Train(ManagedMat^ _train_data, 
		int _tflag,
		ManagedMat^ _responses, 
		ManagedMat^ _var_idx,
		ManagedMat^ _sample_idx, 
		ManagedMat^ _var_type,
		ManagedMat^ _missing_mask,
		BoostParams params)
	{
		return ((CvBoost*)m_pHandle)->train(
			_train_data->ToCvMatPointer(), 
			_tflag,
			_responses->ToCvMatPointer(), 
			_var_idx->ToCvMatPointer(),
			_sample_idx->ToCvMatPointer(), 
			_var_type->ToCvMatPointer(),
			_missing_mask->ToCvMatPointer(),
			*(CvBoostParams*)&params);
	}

	bool Train(ManagedMat^ _train_data, 
		int _tflag,
		ManagedMat^ _responses, 
		ManagedMat^ _var_idx,
		ManagedMat^ _sample_idx, 
		ManagedMat^ _var_type,
		ManagedMat^ _missing_mask)
	{
		return ((CvBoost*)m_pHandle)->train(
			_train_data->ToCvMatPointer(), 
			_tflag,
			_responses->ToCvMatPointer(), 
			_var_idx->ToCvMatPointer(),
			_sample_idx->ToCvMatPointer(), 
			_var_type->ToCvMatPointer(),
			_missing_mask->ToCvMatPointer());
	}

	bool Train(ManagedMat^ _train_data, 
		int _tflag,
		ManagedMat^ _responses, 
		ManagedMat^ _var_idx,
		ManagedMat^ _sample_idx, 
		ManagedMat^ _var_type)
	{
		return ((CvBoost*)m_pHandle)->train(
			_train_data->ToCvMatPointer(), 
			_tflag,
			_responses->ToCvMatPointer(), 
			_var_idx->ToCvMatPointer(),
			_sample_idx->ToCvMatPointer(), 
			_var_type->ToCvMatPointer());
	}

	bool Train(ManagedMat^ _train_data, 
		int _tflag,
		ManagedMat^ _responses, 
		ManagedMat^ _var_idx,
		ManagedMat^ _sample_idx)
	{
		return ((CvDTree*)m_pHandle)->train(
			_train_data->ToCvMatPointer(), 
			_tflag,
			_responses->ToCvMatPointer(), 
			_var_idx->ToCvMatPointer(),
			_sample_idx->ToCvMatPointer());
	}

	bool Train(ManagedMat^ _train_data, 
		int _tflag,
		ManagedMat^ _responses, 
		ManagedMat^ _var_idx)
	{
		return ((CvBoost*)m_pHandle)->train(
			_train_data->ToCvMatPointer(), 
			_tflag,
			_responses->ToCvMatPointer(), 
			_var_idx->ToCvMatPointer());
	}

	bool Train(ManagedMat^ _train_data,  int _tflag, ManagedMat^ _responses)
	{
		return ((CvBoost*)m_pHandle)->train(
			_train_data->ToCvMatPointer(), 
			_tflag,
			_responses->ToCvMatPointer());
	}

	bool Train(ManagedMLData^ data, BoostParams params, bool update)
	{
		return ((CvBoost*)m_pHandle)->train(data->ToCvMLDataPointer(), *(CvBoostParams*)&params, update);
	}

	bool Train(ManagedMLData^ data, BoostParams params)
	{
		return ((CvBoost*)m_pHandle)->train(data->ToCvMLDataPointer(), *(CvBoostParams*)&params);
	}

	bool Train(ManagedMLData^ data)
	{
		return ((CvBoost*)m_pHandle)->train(data->ToCvMLDataPointer());
	}

	float Predict(ManagedMat^ _sample, 
		ManagedMat^ _missing,
		ManagedMat^ weak_responses, 
		Slice slice,
		bool raw_mode, 
		bool return_sum )
	{
		return ((CvBoost*)m_pHandle)->predict(
			_sample->ToCvMatPointer(),
			_missing->ToCvMatPointer(),
			weak_responses->ToCvMatPointer(),
			*(CvSlice*)&slice,
			raw_mode,
			return_sum);
	}

	float Predict(ManagedMat^ _sample, 
		ManagedMat^ _missing,
		ManagedMat^ weak_responses, 
		Slice slice,
		bool raw_mode)
	{
		return ((CvBoost*)m_pHandle)->predict(
			_sample->ToCvMatPointer(),
			_missing->ToCvMatPointer(),
			weak_responses->ToCvMatPointer(),
			*(CvSlice*)&slice,
			raw_mode);
	}

	float Predict(ManagedMat^ _sample, 
		ManagedMat^ _missing,
		ManagedMat^ weak_responses, 
		Slice slice)
	{
		return ((CvBoost*)m_pHandle)->predict(
			_sample->ToCvMatPointer(),
			_missing->ToCvMatPointer(),
			weak_responses->ToCvMatPointer(),
			*(CvSlice*)&slice);
	}

	float Predict(ManagedMat^ _sample, 
		ManagedMat^ _missing,
		ManagedMat^ weak_responses)
	{
		return ((CvBoost*)m_pHandle)->predict(
			_sample->ToCvMatPointer(),
			_missing->ToCvMatPointer(),
			weak_responses->ToCvMatPointer());
	}

	float Predict(ManagedMat^ _sample, 
		ManagedMat^ _missing)
	{
		return ((CvBoost*)m_pHandle)->predict(
			_sample->ToCvMatPointer(),
			_missing->ToCvMatPointer());
	}

	float Predict(ManagedMat^ _sample)
	{
		return ((CvBoost*)m_pHandle)->predict(_sample->ToCvMatPointer());
	}

	float CalcError(ManagedMLData^ _data, MlErrorType type, IntPtr resp)
	{
		return ((CvBoost*)m_pHandle)->calc_error(_data->ToCvMLDataPointer(), (int)type, (vector<float>*)resp.ToPointer());
	}

	float CalcError(ManagedMLData^ _data, MlErrorType type)
	{
		return ((CvBoost*)m_pHandle)->calc_error(_data->ToCvMLDataPointer(), (int)type);
	}

	void Prune(Slice slice)
	{
		return ((CvBoost*)m_pHandle)->prune(*(CvSlice*)&slice);
	}

	ManagedMat^ GetActiveVars(bool absolute_idx)
	{
		return gcnew ManagedMat((CvMat*)((CvBoost*)m_pHandle)->get_active_vars(absolute_idx), false);
	}

	ManagedMat^ GetActiveVars()
	{
		return gcnew ManagedMat((CvMat*)((CvBoost*)m_pHandle)->get_active_vars(), false);
	}

	property ManagedSeq^ WeakPredictors
	{
		ManagedSeq^ get() { return gcnew ManagedSeq((CvSeq*)((CvBoost*)m_pHandle)->get_weak_predictors()); }
	}

	property ManagedMat^ Weights
	{
		ManagedMat^ get() { return gcnew ManagedMat((CvMat*)((CvBoost*)m_pHandle)->get_weights(), false); }
	}

	property ManagedMat^ SubtreeWeights
	{
		ManagedMat^ get() { return gcnew ManagedMat((CvMat*)((CvBoost*)m_pHandle)->get_subtree_weights(), false); }
	}

	property ManagedMat^ WeakResponse
	{
		ManagedMat^ get() { return gcnew ManagedMat((CvMat*)((CvBoost*)m_pHandle)->get_weak_response(), false); }
	}

	property NAMESPACE::BoostParams Params
	{
		NAMESPACE::BoostParams get()
		{
			return *(NAMESPACE::BoostParams*)(&((CvBoost*)m_pHandle)->get_params());
		}
	}

	property ManagedDTreeTrainData^ Data
	{
		ManagedDTreeTrainData^ get() { return gcnew ManagedDTreeTrainData((CvDTreeTrainData*)((CvBoost*)m_pHandle)->get_data(), false); }
	}
};

public ref class ManagedBoostTree : public ManagedDTree
{
internal:
	ManagedBoostTree(CvBoostTree* model, bool own) : ManagedDTree(model, own)
	{
	}

	inline CvBoostTree* ToCvBoostTreePointer()
	{
		return (CvBoostTree*)m_pHandle;
	}

public:
	ManagedBoostTree() : ManagedDTree(new CvBoostTree(), true)
	{
	}

	bool Train(ManagedDTreeTrainData^ _train_data, ManagedMat^ subsample_idx, ManagedBoost^ ensemble)
	{
		return ((CvBoostTree*)m_pHandle)->train(_train_data->ToCvDTreeTrainDataPointer(), subsample_idx->ToCvMatPointer(), ensemble->ToCvBoostPointer());
	}

	void Scale(double s)
	{
		return ((CvBoostTree*)m_pHandle)->scale(s);
	}

	void Read(ManagedFileStorage^ fs, ManagedFileNode^ node, ManagedBoost^ ensemble, ManagedDTreeTrainData^ _data)
	{
		return ((CvBoostTree*)m_pHandle)->read(fs->ToCvFileStoragePointer(), node->ToCvFileNodePointer(), 
			ensemble->ToCvBoostPointer(), _data->ToCvDTreeTrainDataPointer());
	}
};

/****************************************************************************************\
*                              Artificial Neural Networks (ANN)                          *
\****************************************************************************************/
public ref class ManagedANN_MLP : public ManagedStatModel
{
internal:
	ManagedANN_MLP(CvANN_MLP* model, bool own) : ManagedStatModel(model, own)
	{
	}

	inline CvANN_MLP* ToCvANN_MLPPointer()
	{
		return (CvANN_MLP*)m_pHandle;
	}

public:
	ManagedANN_MLP() : ManagedStatModel(new CvANN_MLP(), true)
	{
	}

	ManagedANN_MLP(ManagedMat^ _layer_sizes, ANNActivationFunction _activ_func, double _f_param1, double _f_param2)
		: ManagedStatModel(new CvANN_MLP(_layer_sizes->ToCvMatPointer(), (int)_activ_func, _f_param1, _f_param2), true)
	{
	}

	ManagedANN_MLP(ManagedMat^ _layer_sizes, ANNActivationFunction _activ_func, double _f_param1)
		: ManagedStatModel(new CvANN_MLP(_layer_sizes->ToCvMatPointer(), (int)_activ_func, _f_param1), true)
	{
	}

	ManagedANN_MLP(ManagedMat^ _layer_sizes, ANNActivationFunction _activ_func)
		: ManagedStatModel(new CvANN_MLP(_layer_sizes->ToCvMatPointer(), (int)_activ_func), true)
	{
	}

	ManagedANN_MLP(ManagedMat^ _layer_sizes)
		: ManagedStatModel(new CvANN_MLP(_layer_sizes->ToCvMatPointer()), true)
	{
	}

	void Create(ManagedMat^ _layer_sizes, ANNActivationFunction _activ_func, double _f_param1, double _f_param2)
	{
		return ((CvANN_MLP*)m_pHandle)->create(_layer_sizes->ToCvMatPointer(), (int)_activ_func, _f_param1, _f_param2);
	}

	void Create(ManagedMat^ _layer_sizes, ANNActivationFunction _activ_func, double _f_param1)
	{
		return ((CvANN_MLP*)m_pHandle)->create(_layer_sizes->ToCvMatPointer(), (int)_activ_func, _f_param1);
	}

	void Create(ManagedMat^ _layer_sizes, ANNActivationFunction _activ_func)
	{
		return ((CvANN_MLP*)m_pHandle)->create(_layer_sizes->ToCvMatPointer(), (int)_activ_func);
	}

	void Create(ManagedMat^ _layer_sizes)
	{
		return ((CvANN_MLP*)m_pHandle)->create(_layer_sizes->ToCvMatPointer());
	}

	int Train(ManagedMat^ _inputs, ManagedMat^ _outputs, ManagedMat^ _sample_weights, 
		ManagedMat^ _sample_idx, ANNMLPTrainParams _params, ANNTrainFlag flags)
	{
		return ((CvANN_MLP*)m_pHandle)->train(
			_inputs->ToCvMatPointer(), 
			_outputs->ToCvMatPointer(), 
			_sample_weights->ToCvMatPointer(), 
			_sample_idx->ToCvMatPointer(), 
			*(CvANN_MLP_TrainParams*)&_params, 
			(int)flags);
	}

	int Train(ManagedMat^ _inputs, ManagedMat^ _outputs, ManagedMat^ _sample_weights, 
		ManagedMat^ _sample_idx, ANNMLPTrainParams _params)
	{
		return ((CvANN_MLP*)m_pHandle)->train(
			_inputs->ToCvMatPointer(), 
			_outputs->ToCvMatPointer(), 
			_sample_weights->ToCvMatPointer(), 
			_sample_idx->ToCvMatPointer(), 
			*(CvANN_MLP_TrainParams*)&_params);
	}

	int Train(ManagedMat^ _inputs, ManagedMat^ _outputs, ManagedMat^ _sample_weights, 
		ManagedMat^ _sample_idx)
	{
		return ((CvANN_MLP*)m_pHandle)->train(
			_inputs->ToCvMatPointer(), 
			_outputs->ToCvMatPointer(), 
			_sample_weights->ToCvMatPointer(), 
			_sample_idx->ToCvMatPointer());
	}

	int Train(ManagedMat^ _inputs, ManagedMat^ _outputs, ManagedMat^ _sample_weights)
	{
		return ((CvANN_MLP*)m_pHandle)->train(
			_inputs->ToCvMatPointer(), 
			_outputs->ToCvMatPointer(), 
			_sample_weights->ToCvMatPointer());
	}

	float Predict(ManagedMat^ _inputs, ManagedMat^ _outputs)
	{
		return ((CvANN_MLP*)m_pHandle)->predict(_inputs->ToCvMatPointer(), _outputs->ToCvMatPointer());
	}

	property int LayerCount
	{
		int get() { return ((CvANN_MLP*)m_pHandle)->get_layer_count(); }
	}

	property ManagedMat^ LayerSizes
	{
		ManagedMat^ get() { return gcnew ManagedMat((CvMat*)((CvANN_MLP*)m_pHandle)->get_layer_sizes(), false); }
	}

	array<double>^ GetWeights(int layer)
	{
		double* pweights = ((CvANN_MLP*)m_pHandle)->get_weights(layer);
		array<double>^ aweight = nullptr;
		if (pweights != NULL)
		{
			const CvMat* player_sizes = ((CvANN_MLP*)m_pHandle)->get_layer_sizes();
			int laycount = ((CvANN_MLP*)m_pHandle)->get_layer_count();
			int* lnode = player_sizes->data.i;
			if (layer == 0)
			{
				aweight = gcnew array<double>(lnode[0] * 2);
			}
			else if (layer == laycount)
			{
				aweight = gcnew array<double>(lnode[laycount - 1] * 2);
			}
			else
			{
				aweight = gcnew array<double>(lnode[layer - 1] + 1 * lnode[layer]);
			}
			for (int i = 0; i < aweight->Length; i++)
			{
				aweight[i] = pweights[i];
			}
		}
		return aweight;
	}
};
//////////////////////////////////////////////////////////////////////////
NAMESPACE_END
//////////////////////////////////////////////////////////////////////////